package cn.tedu.blog.server.filter;

import cn.tedu.blog.server.security.LoginPrincipal;
import cn.tedu.blog.server.utils.JsonResult;
import cn.tedu.blog.server.utils.JwtUtils;
import cn.tedu.blog.server.utils.ServiceCode;
import com.alibaba.fastjson.JSON;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * @Author: 雷茂林
 * @Date:2022/7/15 22:01
 * @Description:
 */
@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    // 最终，过滤器可以选择阻止或放行
    // 如果选择阻止，则后续的所有组件都不会执行
    // 如果选择放行，会执行过滤器链中剩余的部分，甚至继续向后执行到控制器等组件
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //此方法是任何请求都会执行的方法
        log.debug("已执行JwtAuthorizationFilter");

        // 清除Security的上下文
        // 如果不清楚，只要此前存入的信息，即使后续不携带JWT，上下文中的登录信息依然存在
        SecurityContextHolder.clearContext();


        // 从请求头中获取JWT
        String jwt = request.getHeader("Authorization");
        log.debug("从请求头中获取的JWT数据：{}", jwt);


        log.error("请求路径："+request.getRequestURI());
        // 先判断是否获取到了有效的JWT数据，如果无JWT数据，直接放行
        if (!StringUtils.hasText(jwt)){
            log.debug("请求头中的数据是无效的，直接放行");
            System.out.println("请求路径："+request.getRequestURI());
            filterChain.doFilter(request,response);
            return;
        }

        //如果获取到了有效的JWT，则尝试解析
        Claims claims = null;
        try {
            claims = JwtUtils.parse(jwt);
        }catch (ExpiredJwtException e) {
            log.debug("解析JWT失败，JWT过期：{}, {}", e.getClass().getName(), e.getMessage());
            String errorMessage = "登录信息已过期，请重新登录！";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_EXPIRED, errorMessage);
            String JsonResulttring = JSON.toJSONString(jsonResult);
            log.debug("将向客户端响应：{}", JsonResulttring);
            response.setContentType("application/json; charset=UTF-8");
            response.getWriter().println(JsonResulttring);
            return;
        } catch (SignatureException e) {
            log.debug("解析JWT失败，签名错误：{}, {}", e.getClass().getName(), e.getMessage());
            String errorMessage = "获取登录信息失败，请重新登录！";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_INVALID, errorMessage);
            String JsonResulttring = JSON.toJSONString(jsonResult);
            log.debug("将向客户端响应：{}", JsonResulttring);
            response.setContentType("application/json; charset=UTF-8");
            response.getWriter().println(JsonResulttring);
            return;
        } catch (MalformedJwtException e) {
            log.debug("解析JWT失败，JWT数据有误：{}, {}", e.getClass().getName(), e.getMessage());
            String errorMessage = "获取登录信息失败，请重新登录！";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_INVALID, errorMessage);
            String JsonResulttring = JSON.toJSONString(jsonResult);
            log.debug("将向客户端响应：{}", JsonResulttring);
            response.setContentType("application/json; charset=UTF-8");
            response.getWriter().println(JsonResulttring);
            return;
        } catch (Throwable e) {
            log.debug("解析JWT失败，错误详情：{}, {}", e.getClass().getName(), e.getMessage());
            String errorMessage = "获取登录信息失败，请重新登录！";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_INVALID, errorMessage);
            String JsonResulttring = JSON.toJSONString(jsonResult);
            log.debug("将向客户端响应：{}", JsonResulttring);
            response.setContentType("application/json; charset=UTF-8");
            response.getWriter().println(JsonResulttring);
            return;
        }

        //从解析JWT的结果中取出登录的管理员信息
        Object id = claims.get("id");
        log.debug("从JWT中解析得到的用户ID：{}",id);
        Object username = claims.get("username");
        log.debug("从JWT中解析得到用户名：{}",username);
        Object authoritiesString = claims.get("authorities");
        log.debug("从JWT中解析得到权限列表：{}",authoritiesString);

        // 将得到的权限列表字符串封装为GrantedAuthority集合
        List<SimpleGrantedAuthority> authorities =
                JSON.parseArray(authoritiesString.toString(),SimpleGrantedAuthority.class);


        //准备当前登录用户的当事人信息
        LoginPrincipal loginPrincipal = new LoginPrincipal();
        loginPrincipal.setId(Long.parseLong(id.toString()));
        loginPrincipal.setUsername(username.toString());

        //当解析成功后，应该将存入到spring security的上下文中
        Authentication authentication =
                new UsernamePasswordAuthenticationToken(loginPrincipal,null,authorities);
        SecurityContext securityContext = SecurityContextHolder.getContext();
        log.debug("即将向Security的上下文中写入：{}", authentication);
        securityContext.setAuthentication(authentication);

        // 以下代码将执行“放行”
        filterChain.doFilter(request, response);
    }
}
